/*
 * spi.c
 *
 *  Created on: Sep 30, 2013
 *      Author: Ken Arok
 */


#include "config_board.h"

#if BOARD_2_1_USED

#include <avr/io.h>
#include "gpio.h"
#include "timers_hw.h"
#include "spi.h"


#define ADO_PROTOCOL_SOH	0x01
#define ADO_PROTOCOL_STX	0x02
#define ADO_PROTOCOL_ETX	0x03
#define ADO_PROTOCOL_EOT	0x04
#define ADO_PROTOCOL_ENQ	0x05
#define ADO_PROTOCOL_ACK	0x06


#define ADO_COMMAND_FLAG_MASK	0x7F

#define ADO_TOKEN_SOH_IDX	0
#define ADO_TOKEN_CMD_IDX	1
#define ADO_TOKEN_ENQ_IDX	2


#define SPI_SELECT_MODE_MASK	0xF3
#define SPI_MODE0_BIT_MASK		0x00
#define SPI_MODE1_BIT_MASK		0x04
#define SPI_MODE2_BIT_MASK		0x08
#define SPI_MODE3_BIT_MASK		0x0C


//! Protocol token.
volatile uint8_t ado_token[3] = {0, 0, 0};

//! Receive data buffer.
volatile uint8_t ado_data[MAX_DATA];

//! Index for token data.
static uint8_t token_idx = 0;

//! Index for response data.
static uint8_t response_idx= 0;

//! Index for incoming data.
static uint8_t data_idx = 0;

//! Response message.
static ado_respons_t *response_msg = NULL;

//! Flag of message
static volatile bool is_token =  true;
static volatile bool is_data = false;
static volatile bool is_response = false;
static volatile bool is_bcc =  false;
//! Global Flag
volatile bool spi_message_is_received = false;
volatile bool spi_command_is_received = false;
volatile bool spi_rx_buffer_is_ready = true;

/** \brief SPI Interrupt Handler.
 *
 */
static void spi_interrupt_handler(void);

/** \brief Initialize SPI.
 *
 */
static void spi_init(void);

/* ------------------------------------------------------------------------------------ */
/* SPI Interrupt. */
ISR(SPI_STC_vect)
{
	spi_interrupt_handler();
}

void spi_slave_init(void)
{
	/* Configure SPI GPIO. */
	gpio_configure_input(MEGA128_CS_GPIO);
	gpio_configure_input(SPI_SS_GPIO);
	gpio_configure_input(SPI_MOSI_GPIO);
	gpio_configure_input(SPI_CLK_GPIO);
	gpio_configure_output(SPI_MISO_GPIO);
	gpio_enable_pullup(SPI_SS_GPIO);
	gpio_enable_pullup(SPI_MOSI_GPIO);
	gpio_enable_pullup(SPI_CLK_GPIO);
	gpio_enable_pullup(MEGA128_CS_GPIO);

	/* Initialize SPI as Slave. */
	spi_init();
}


/* ------------------------------------------------------------------------------------ */
static void spi_interrupt_handler(void)
{
	uint8_t _status, _data;

	_status = SPSR;
	/* Copy register data. */
	_data = SPDR;

	/* Process data if no error. */
	if(!(_status & (1 << WCOL))) {
		/* It is token. */
		if(is_token == true) {
			/* Process the token. */
			switch(_data) {
				case ADO_PROTOCOL_SOH:
					ado_token[0] = _data;
					token_idx = 1;
				break;

				case ADO_PROTOCOL_ENQ:
					ado_token[token_idx] = _data;
					token_idx = 0;
					spi_command_is_received =  true;

					/* Command without response but the next is data. */
					if(ado_token[ADO_TOKEN_CMD_IDX] & ADO_COMMAND_FLAG_MASK) {
						is_response = false;
						data_idx = 0;
						if(spi_rx_buffer_is_ready == true) {
							is_data = true;
							is_token = false;
							/* Acknowledgment for ready to receive. */
							SPDR = ADO_PROTOCOL_ACK;
						}
						else {
							is_data = false;
							is_token = true;
							/* Buffer is busy. */
							SPDR = ADO_PROTOCOL_EOT;
						}
					}
					/* Command with response. */
					else {
						is_data = false;
						is_token = false;
						is_response = true;
						response_idx = 0;
					}
				break;

				case ADO_PROTOCOL_EOT:
					is_data = false;
					is_token = true;
					is_response = false;
					response_idx = 0;
					data_idx = 0;
					token_idx = 0;
				break;

				case ADO_PROTOCOL_ACK:
					is_data = false;
					is_token = true;
					is_response = false;
					data_idx = 0;
					token_idx = 0;
					/* End of Transmission. */
					SPDR = ADO_PROTOCOL_EOT;
				break;

				default:
					if(token_idx) {
						ado_token[token_idx] = _data;
						token_idx++;
					}
				break;
			}
		}
		/* It is data */
		else if(is_data == true) {
			switch(_data) {
				case ADO_PROTOCOL_STX:
					ado_data[data_idx] = _data;
					data_idx = 1;
					is_bcc = false;
				break;

				case ADO_PROTOCOL_ETX:
					ado_data[data_idx] = _data;
					data_idx++;
					is_bcc = true;
				break;

				default:
					if(is_bcc == true) {
						is_bcc = false;
						ado_data[data_idx] = _data;
						is_data =  false;
						is_token = true;
						spi_message_is_received = true;
						/* Acknowledgment message is already received. */
						SPDR = ADO_PROTOCOL_ACK;
					}
					else {
						if(data_idx) {
							ado_data[data_idx] = _data;
							data_idx++;
							if(data_idx >= MAX_DATA) {
								is_data =  false;
								is_token = true;
							}
						}
					}
				break;
			}
		}
		/* It is response. */
		else if(is_response == true) {
			/* Check if response message is ready. */
			if(response_msg->size_t) {
				SPDR = response_msg->pBuf[response_idx];
				response_idx++;
				response_msg->size_t--;
			}
			else {
				SPDR = 0xFF;
			}
		}
		/* Dummy response. */
		else {
			SPDR = 0xFF;
		}
	}
}


static void spi_init(void)
{
	/* Setup data order MSB first. */
	SPCR &= ~(1 << DORD);
	/* Default is SPI mode 0. */
	SPCR &= SPI_SELECT_MODE_MASK;
	SPCR |= SPI_MODE0_BIT_MASK;
	/* SPI Clock SCK/16 */
	SPCR &= 0xFC;
	SPCR |= (1 << SPR0);

	/* SPI is Slave. */
	SPCR &= ~(1 << MSTR);

	/* Enable SPI. */
	SPCR |= ((1 << SPE) | (1 << SPIE));
}


#endif /* BOARD_2_1_USED */
